﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using FITClub.Util;
using Microsoft.Xna.Framework.Content;
using xna = Microsoft.Xna.Framework;
using xnag = Microsoft.Xna.Framework.Graphics;

namespace FITClub.LevelBuilder.Library
{
    public class LibraryItem : IDisposable
    {
        public string ContentPath { get; set; }

        public string PreviewPath { get; set; }

        public string DisplayName { get; set; }

        [XmlIgnore]
        public Image PreviewImage { get; set; }

        [XmlIgnore]
        public xnag.Model Model { get; private set; }

        public void LoadImage()
        {
            this.DisposeImage();
            this.PreviewImage = Bitmap.FromFile(this.PreviewPath);
        }

        private void DisposeImage()
        {
            if (PreviewImage != null)
            {
                PreviewImage.Dispose();
                PreviewImage = null;
            }
        }

        public void LoadModel(ContentManager content)
        {
            this.Model = content.Load<xnag.Model>(this.ContentPath);
        }

        public void Dispose()
        {
            DisposeImage();
        }
    }

    public class ModelLibrary : IDisposable
    {
        public const string Path = "ModelLibrary\\Library.xml";

        [XmlArray]
        public List<LibraryItem> Platforms { get; set; }

        [XmlArray]
        public List<LibraryItem> BackgroundItems { get; set; }

        [XmlArray]
        public List<LibraryItem> Powerups { get; set; }

        [XmlIgnore]
        public xnag.RenderTarget2D ImageTarget { get; private set; }

        private xna.Matrix view;
        private xna.Matrix projection;
        private xna.Matrix world;
        private float cameraDistance = 3.0f;

        private void InitializeTarget(xnag.GraphicsDevice device, int width = 128, int height = 96)
        {
            if (ImageTarget != null)
            {
                ImageTarget.Dispose();
            }
            this.projection = xna.Matrix.CreatePerspectiveFieldOfView(xna.MathHelper.PiOver4, (float)width / (float)height, 0.1f, 1000.0f);
            this.view = xna.Matrix.CreateLookAt(                                                                                 //nastavení kamery část2
                           new xna.Vector3(0, 1, this.cameraDistance),
                           xna.Vector3.Zero,
                           xna.Vector3.Up
                       );

            this.world = new MatrixTransforms() { Rotation = xna.Vector3.Zero, Scale = xna.Vector3.One, Translation = xna.Vector3.Zero }.CreateTransform();
            ImageTarget = new xnag.RenderTarget2D(device, width, height, false, xnag.SurfaceFormat.Color, xnag.DepthFormat.Depth24Stencil8);
        }

        public LibraryItem FindByPath(string contentPath, List<LibraryItem> items)
        {
            return items.Single((LibraryItem li) => li.ContentPath == contentPath);
        }

        public Image DrawToBitmap(xnag.Model model, xna.Matrix mmm)
        {
            ImageTarget.GraphicsDevice.SetRenderTarget(ImageTarget);
            ImageTarget.GraphicsDevice.Clear(xna.Color.SteelBlue);
            model.Draw(mmm, this.view, this.projection);
            ImageTarget.GraphicsDevice.SetRenderTarget(null);
            using (var ms = new MemoryStream())
            {
                ImageTarget.SaveAsPng(ms, ImageTarget.Width, ImageTarget.Height);
                return Bitmap.FromStream(ms);
            }
        }

        public static ModelLibrary Load(bool loadImages, xnag.GraphicsDevice device, ContentManager content)
        {
            using (var fs = File.OpenRead(Path))
            {
                XmlSerializer xser = new XmlSerializer(typeof(ModelLibrary));
                ModelLibrary pc = (ModelLibrary)xser.Deserialize(fs);
                pc.InitializeTarget(device);
                pc.LoadModels(content, pc.BackgroundItems);
                pc.LoadModels(content, pc.Platforms);
                pc.LoadModels(content, pc.Powerups);
                if (loadImages)
                {
                    pc.LoadImages(pc.BackgroundItems);
                    pc.LoadImages(pc.Platforms);
                    pc.LoadImages(pc.Powerups);
                }
                return pc;
            }
        }

        private void LoadModels(ContentManager content, List<LibraryItem> list)
        {
            foreach (var li in list)
            {
                try
                {
                    li.LoadModel(content);
                }
                catch (Exception exc)
                {
                    Console.Error.WriteLine(exc.Message);
                }
            }
        }

        private void LoadImages(List<LibraryItem> config)
        {
            if (config != null)
            {
                foreach (var item in config)
                {
                    this.LoadItemPreview(item);
                }
            }
        }

        /// <summary>
        /// Tries to load previes from file. If failed, creates instatnt render into Image
        /// </summary>
        /// <param name="item"></param>
        public void LoadItemPreview(LibraryItem item)
        {
            try
            {
                item.LoadImage();
            }
            catch (Exception exc)
            {
                Console.WriteLine("{1} Load failed: {0}", item.PreviewPath, exc.Message);
                try
                {
                    item.PreviewImage = this.DrawToBitmap(item.Model, this.world);
                }
                catch (Exception exc1)
                {
                    ImageTarget.GraphicsDevice.SetRenderTarget(null);
                    Console.WriteLine("Draw failed: {0}", exc1.Message);
                }
            }
        }

        /// <summary>
        /// Saves library into XML file.
        /// </summary>
        /// <param name="path"></param>
        public void Save(string path = Path)
        {
            using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write))
            {
                XmlSerializer xser = new XmlSerializer(typeof(ModelLibrary));
                xser.Serialize(fs, this);
            }
        }

        /// <summary>
        /// Clean up resources.
        /// </summary>
        public void Dispose()
        {
            Action<LibraryItem> dispose = new Action<LibraryItem>((LibraryItem li) => li.Dispose());
            this.BackgroundItems.ForEach(dispose);
            this.Platforms.ForEach(dispose);
            this.Powerups.ForEach(dispose);
        }
    }
}